{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "#!/usr/bin/env python\n",
    "# -*- coding: utf-8 -*-\n",
    "\n",
    "\"\"\"\n",
    "这是一个简单的， 轻量级的， WSGI兼容(Web Server Gateway Interface)的web 框架\n",
    "WSGI概要：\n",
    "    工作方式： WSGI server -----> WSGI 处理函数\n",
    "    作用：将HTTP原始的请求、解析、响应 这些交给WSGI server 完成，\n",
    "          让我们专心用Python编写Web业务，也就是 WSGI 处理函数\n",
    "          所以WSGI 是HTTP的一种高级封装。\n",
    "    例子：\n",
    "        wsgi 处理函数\n",
    "            def application(environ, start_response):\n",
    "                method = environ['REQUEST_METHOD']\n",
    "                path = environ['PATH_INFO']\n",
    "                if method=='GET' and path=='/':\n",
    "                return handle_home(environ, start_response)\n",
    "                if method=='POST' and path='/signin':\n",
    "                return handle_signin(environ, start_response)\n",
    "        wsgi server\n",
    "            def run(self, port=9000, host='127.0.0.1'):\n",
    "                from wsgiref.simple_server import make_server\n",
    "                server = make_server(host, port, application)\n",
    "                server.serve_forever()\n",
    "设计web框架的原因：\n",
    "    1. WSGI提供的接口虽然比HTTP接口高级了不少，但和Web App的处理逻辑比，还是比较低级，\n",
    "       我们需要在WSGI接口之上能进一步抽象，让我们专注于用一个函数处理一个URL，\n",
    "       至于URL到函数的映射，就交给Web框架来做。\n",
    "设计web框架接口：\n",
    "    1. URL路由： 用于URL 到 处理函数的映射\n",
    "    2. URL拦截： 用于根据URL做权限检测\n",
    "    3. 视图： 用于HTML页面生成\n",
    "    4. 数据模型： 用于抽取数据（见models模块）\n",
    "    5. 事物数据：request数据和response数据的封装（thread local）\n",
    "\"\"\"\n",
    "\n",
    "import types, os, re, cgi, sys, time, datetime, functools, mimetypes, threading, logging, traceback, urllib\n",
    "\n",
    "from db import Dict\n",
    "import utils\n",
    "\n",
    "try:\n",
    "    from cStringIO import StringIO\n",
    "except ImportError:\n",
    "    from StringIO import StringIO\n",
    "\n",
    "#################################################################\n",
    "# 实现事物数据接口, 实现request 数据和response数据的存储,\n",
    "# 是一个全局ThreadLocal对象\n",
    "#################################################################\n",
    "ctx = threading.local()\n",
    "\n",
    "\n",
    "_RE_RESPONSE_STATUS = re.compile(r'^\\d\\d\\d(\\ [\\w\\ ]+)?$')\n",
    "_HEADER_X_POWERED_BY = ('X-Powered-By', 'transwarp/1.0')\n",
    "\n",
    "\n",
    "#  用于时区转换\n",
    "_TIMEDELTA_ZERO = datetime.timedelta(0)\n",
    "_RE_TZ = re.compile('^([\\+\\-])([0-9]{1,2})\\:([0-9]{1,2})$')\n",
    "\n",
    "# response status\n",
    "_RESPONSE_STATUSES = {\n",
    "    # Informational\n",
    "    100: 'Continue',\n",
    "    101: 'Switching Protocols',\n",
    "    102: 'Processing',\n",
    "\n",
    "    # Successful\n",
    "    200: 'OK',\n",
    "    201: 'Created',\n",
    "    202: 'Accepted',\n",
    "    203: 'Non-Authoritative Information',\n",
    "    204: 'No Content',\n",
    "    205: 'Reset Content',\n",
    "    206: 'Partial Content',\n",
    "    207: 'Multi Status',\n",
    "    226: 'IM Used',\n",
    "\n",
    "    # Redirection\n",
    "    300: 'Multiple Choices',\n",
    "    301: 'Moved Permanently',\n",
    "    302: 'Found',\n",
    "    303: 'See Other',\n",
    "    304: 'Not Modified',\n",
    "    305: 'Use Proxy',\n",
    "    307: 'Temporary Redirect',\n",
    "\n",
    "    # Client Error\n",
    "    400: 'Bad Request',\n",
    "    401: 'Unauthorized',\n",
    "    402: 'Payment Required',\n",
    "    403: 'Forbidden',\n",
    "    404: 'Not Found',\n",
    "    405: 'Method Not Allowed',\n",
    "    406: 'Not Acceptable',\n",
    "    407: 'Proxy Authentication Required',\n",
    "    408: 'Request Timeout',\n",
    "    409: 'Conflict',\n",
    "    410: 'Gone',\n",
    "    411: 'Length Required',\n",
    "    412: 'Precondition Failed',\n",
    "    413: 'Request Entity Too Large',\n",
    "    414: 'Request URI Too Long',\n",
    "    415: 'Unsupported Media Type',\n",
    "    416: 'Requested Range Not Satisfiable',\n",
    "    417: 'Expectation Failed',\n",
    "    418: \"I'm a teapot\",\n",
    "    422: 'Unprocessable Entity',\n",
    "    423: 'Locked',\n",
    "    424: 'Failed Dependency',\n",
    "    426: 'Upgrade Required',\n",
    "\n",
    "    # Server Error\n",
    "    500: 'Internal Server Error',\n",
    "    501: 'Not Implemented',\n",
    "    502: 'Bad Gateway',\n",
    "    503: 'Service Unavailable',\n",
    "    504: 'Gateway Timeout',\n",
    "    505: 'HTTP Version Not Supported',\n",
    "    507: 'Insufficient Storage',\n",
    "    510: 'Not Extended',\n",
    "}\n",
    "\n",
    "_RESPONSE_HEADERS = (\n",
    "    'Accept-Ranges',\n",
    "    'Age',\n",
    "    'Allow',\n",
    "    'Cache-Control',\n",
    "    'Connection',\n",
    "    'Content-Encoding',\n",
    "    'Content-Language',\n",
    "    'Content-Length',\n",
    "    'Content-Location',\n",
    "    'Content-MD5',\n",
    "    'Content-Disposition',\n",
    "    'Content-Range',\n",
    "    'Content-Type',\n",
    "    'Date',\n",
    "    'ETag',\n",
    "    'Expires',\n",
    "    'Last-Modified',\n",
    "    'Link',\n",
    "    'Location',\n",
    "    'P3P',\n",
    "    'Pragma',\n",
    "    'Proxy-Authenticate',\n",
    "    'Refresh',\n",
    "    'Retry-After',\n",
    "    'Server',\n",
    "    'Set-Cookie',\n",
    "    'Strict-Transport-Security',\n",
    "    'Trailer',\n",
    "    'Transfer-Encoding',\n",
    "    'Vary',\n",
    "    'Via',\n",
    "    'Warning',\n",
    "    'WWW-Authenticate',\n",
    "    'X-Frame-Options',\n",
    "    'X-XSS-Protection',\n",
    "    'X-Content-Type-Options',\n",
    "    'X-Forwarded-Proto',\n",
    "    'X-Powered-By',\n",
    "    'X-UA-Compatible',\n",
    ")\n",
    "\n",
    "\n",
    "class UTC(datetime.tzinfo):\n",
    "    \"\"\"\n",
    "    tzinfo 是一个基类，用于给datetime对象分配一个时区\n",
    "    使用方式是 把这个子类对象传递给datetime.tzinfo属性\n",
    "    传递方法有2种：\n",
    "        １.　初始化的时候传入\n",
    "            datetime(2009,2,17,19,10,2,tzinfo=tz0)\n",
    "        ２.　使用datetime对象的 replace方法传入，从新生成一个datetime对象\n",
    "            datetime.replace(tzinfo= tz0）\n",
    "    >>> tz0 = UTC('+00:00')\n",
    "    >>> tz0.tzname(None)\n",
    "    'UTC+00:00'\n",
    "    >>> tz8 = UTC('+8:00')\n",
    "    >>> tz8.tzname(None)\n",
    "    'UTC+8:00'\n",
    "    >>> tz7 = UTC('+7:30')\n",
    "    >>> tz7.tzname(None)\n",
    "    'UTC+7:30'\n",
    "    >>> tz5 = UTC('-05:30')\n",
    "    >>> tz5.tzname(None)\n",
    "    'UTC-05:30'\n",
    "    >>> from datetime import datetime\n",
    "    >>> u = datetime.utcnow().replace(tzinfo=tz0)\n",
    "    >>> l1 = u.astimezone(tz8)\n",
    "    >>> l2 = u.replace(tzinfo=tz8)\n",
    "    >>> d1 = u - l1\n",
    "    >>> d2 = u - l2\n",
    "    >>> d1.seconds\n",
    "    0\n",
    "    >>> d2.seconds\n",
    "    28800\n",
    "    \"\"\"\n",
    "\n",
    "    def __init__(self, utc):\n",
    "        utc = str(utc.strip().upper())\n",
    "        mt = _RE_TZ.match(utc)\n",
    "        if mt:\n",
    "            minus = mt.group(1) == '-'\n",
    "            h = int(mt.group(2))\n",
    "            m = int(mt.group(3))\n",
    "            if minus:\n",
    "                h, m = (-h), (-m)\n",
    "            self._utcoffset = datetime.timedelta(hours=h, minutes=m)\n",
    "            self._tzname = 'UTC%s' % utc\n",
    "        else:\n",
    "            raise ValueError('bad utc time zone')\n",
    "\n",
    "    def utcoffset(self, dt):\n",
    "        \"\"\"\n",
    "        表示与标准时区的 偏移量\n",
    "        \"\"\"\n",
    "        return self._utcoffset\n",
    "\n",
    "    def dst(self, dt):\n",
    "        \"\"\"\n",
    "        Daylight Saving Time 夏令时\n",
    "        \"\"\"\n",
    "        return _TIMEDELTA_ZERO\n",
    "\n",
    "    def tzname(self, dt):\n",
    "        \"\"\"\n",
    "        所在时区的名字\n",
    "        \"\"\"\n",
    "        return self._tzname\n",
    "\n",
    "    def __str__(self):\n",
    "        return 'UTC timezone info object (%s)' % self._tzname\n",
    "\n",
    "    __repr__ = __str__\n",
    "\n",
    "\n",
    "UTC_0 = UTC('+00:00')\n",
    "\n",
    "\n",
    "# 用于异常处理\n",
    "class _HttpError(Exception):\n",
    "    \"\"\"\n",
    "    HttpError that defines http error code.\n",
    "    >>> e = _HttpError(404)\n",
    "    >>> e.status\n",
    "    '404 Not Found'\n",
    "    \"\"\"\n",
    "    def __init__(self, code):\n",
    "        \"\"\"\n",
    "        Init an HttpError with response code.\n",
    "        \"\"\"\n",
    "        super(_HttpError, self).__init__()\n",
    "        self.status = '%d %s' % (code, _RESPONSE_STATUSES[code])\n",
    "        self._headers = None\n",
    "\n",
    "    def header(self, name, value):\n",
    "        \"\"\"\n",
    "        添加header， 如果header为空则 添加powered by header\n",
    "        \"\"\"\n",
    "        if not self._headers:\n",
    "            self._headers = [_HEADER_X_POWERED_BY]\n",
    "        self._headers.append((name, value))\n",
    "\n",
    "    @property\n",
    "    def headers(self):\n",
    "        \"\"\"\n",
    "        使用setter方法实现的 header属性\n",
    "        \"\"\"\n",
    "        if hasattr(self, '_headers'):\n",
    "            return self._headers\n",
    "        return []\n",
    "\n",
    "    def __str__(self):\n",
    "        return self.status\n",
    "\n",
    "    __repr__ = __str__\n",
    "\n",
    "\n",
    "class _RedirectError(_HttpError):\n",
    "    \"\"\"\n",
    "    RedirectError that defines http redirect code.\n",
    "    >>> e = _RedirectError(302, 'http://www.apple.com/')\n",
    "    >>> e.status\n",
    "    '302 Found'\n",
    "    >>> e.location\n",
    "    'http://www.apple.com/'\n",
    "    \"\"\"\n",
    "    def __init__(self, code, location):\n",
    "        \"\"\"\n",
    "        Init an HttpError with response code.\n",
    "        \"\"\"\n",
    "        super(_RedirectError, self).__init__(code)\n",
    "        self.location = location\n",
    "\n",
    "    def __str__(self):\n",
    "        return '%s, %s' % (self.status, self.location)\n",
    "\n",
    "    __repr__ = __str__\n",
    "\n",
    "\n",
    "class HttpError(object):\n",
    "    \"\"\"\n",
    "    HTTP Exceptions\n",
    "    \"\"\"\n",
    "    @staticmethod\n",
    "    def badrequest():\n",
    "        \"\"\"\n",
    "        Send a bad request response.\n",
    "        >>> raise HttpError.badrequest()\n",
    "        Traceback (most recent call last):\n",
    "          ...\n",
    "        _HttpError: 400 Bad Request\n",
    "        \"\"\"\n",
    "        return _HttpError(400)\n",
    "\n",
    "    @staticmethod\n",
    "    def unauthorized():\n",
    "        \"\"\"\n",
    "        Send an unauthorized response.\n",
    "        >>> raise HttpError.unauthorized()\n",
    "        Traceback (most recent call last):\n",
    "          ...\n",
    "        _HttpError: 401 Unauthorized\n",
    "        \"\"\"\n",
    "        return _HttpError(401)\n",
    "\n",
    "    @staticmethod\n",
    "    def forbidden():\n",
    "        \"\"\"\n",
    "        Send a forbidden response.\n",
    "        >>> raise HttpError.forbidden()\n",
    "        Traceback (most recent call last):\n",
    "          ...\n",
    "        _HttpError: 403 Forbidden\n",
    "        \"\"\"\n",
    "        return _HttpError(403)\n",
    "\n",
    "    @staticmethod\n",
    "    def notfound():\n",
    "        \"\"\"\n",
    "        Send a not found response.\n",
    "        >>> raise HttpError.notfound()\n",
    "        Traceback (most recent call last):\n",
    "          ...\n",
    "        _HttpError: 404 Not Found\n",
    "        \"\"\"\n",
    "        return _HttpError(404)\n",
    "\n",
    "    @staticmethod\n",
    "    def conflict():\n",
    "        \"\"\"\n",
    "        Send a conflict response.\n",
    "        >>> raise HttpError.conflict()\n",
    "        Traceback (most recent call last):\n",
    "          ...\n",
    "        _HttpError: 409 Conflict\n",
    "        \"\"\"\n",
    "        return _HttpError(409)\n",
    "\n",
    "    @staticmethod\n",
    "    def internalerror():\n",
    "        \"\"\"\n",
    "        Send an internal error response.\n",
    "        >>> raise HttpError.internalerror()\n",
    "        Traceback (most recent call last):\n",
    "          ...\n",
    "        _HttpError: 500 Internal Server Error\n",
    "        \"\"\"\n",
    "        return _HttpError(500)\n",
    "\n",
    "    @staticmethod\n",
    "    def redirect(location):\n",
    "        \"\"\"\n",
    "        Do permanent redirect.\n",
    "        >>> raise HttpError.redirect('http://www.itranswarp.com/')\n",
    "        Traceback (most recent call last):\n",
    "          ...\n",
    "        _RedirectError: 301 Moved Permanently, http://www.itranswarp.com/\n",
    "        \"\"\"\n",
    "        return _RedirectError(301, location)\n",
    "\n",
    "    @staticmethod\n",
    "    def found(location):\n",
    "        \"\"\"\n",
    "        Do temporary redirect.\n",
    "        >>> raise HttpError.found('http://www.itranswarp.com/')\n",
    "        Traceback (most recent call last):\n",
    "          ...\n",
    "        _RedirectError: 302 Found, http://www.itranswarp.com/\n",
    "        \"\"\"\n",
    "        return _RedirectError(302, location)\n",
    "\n",
    "    @staticmethod\n",
    "    def seeother(location):\n",
    "        \"\"\"\n",
    "        Do temporary redirect.\n",
    "        >>> raise HttpError.seeother('http://www.itranswarp.com/')\n",
    "        Traceback (most recent call last):\n",
    "          ...\n",
    "        _RedirectError: 303 See Other, http://www.itranswarp.com/\n",
    "        >>> e = HttpError.seeother('http://www.itranswarp.com/seeother?r=123')\n",
    "        >>> e.location\n",
    "        'http://www.itranswarp.com/seeother?r=123'\n",
    "        \"\"\"\n",
    "        return _RedirectError(303, location)\n",
    "\n",
    "\n",
    "_RESPONSE_HEADER_DICT = dict(zip(map(lambda x: x.upper(), _RESPONSE_HEADERS), _RESPONSE_HEADERS))\n",
    "\n",
    "\n",
    "class Request(object):\n",
    "    \"\"\"\n",
    "    请求对象， 用于获取所有http请求信息。\n",
    "    \"\"\"\n",
    "\n",
    "    def __init__(self, environ):\n",
    "        \"\"\"\n",
    "        environ  wsgi处理函数里面的那个 environ\n",
    "        wsgi server调用 wsgi 处理函数时传入的\n",
    "        包含了用户请求的所有数据\n",
    "        \"\"\"\n",
    "        self._environ = environ\n",
    "\n",
    "    def _parse_input(self):\n",
    "        \"\"\"\n",
    "        将通过wsgi 传入过来的参数，解析成一个字典对象 返回\n",
    "        比如： Request({'REQUEST_METHOD':'POST', 'wsgi.input':StringIO('a=1&b=M%20M&c=ABC&c=XYZ&e=')})\n",
    "            这里解析的就是 wsgi.input 对象里面的字节流\n",
    "        \"\"\"\n",
    "        def _convert(item):\n",
    "            if isinstance(item, list):\n",
    "                return [utils.to_unicode(i.value) for i in item]\n",
    "            if item.filename:\n",
    "                return MultipartFile(item)\n",
    "            return utils.to_unicode(item.value)\n",
    "        fs = cgi.FieldStorage(fp=self._environ['wsgi.input'], environ=self._environ, keep_blank_values=True)\n",
    "        inputs = dict()\n",
    "        for key in fs:\n",
    "            inputs[key] = _convert(fs[key])\n",
    "        return inputs\n",
    "\n",
    "    def _get_raw_input(self):\n",
    "        \"\"\"\n",
    "        将从wsgi解析出来的 数据字典，添加为Request对象的属性\n",
    "        然后 返回该字典\n",
    "        \"\"\"\n",
    "        if not hasattr(self, '_raw_input'):\n",
    "            self._raw_input = self._parse_input()\n",
    "        return self._raw_input\n",
    "\n",
    "    def __getitem__(self, key):\n",
    "        \"\"\"\n",
    "        实现通过键值访问Request对象里面的数据，如果该键有多个值，则返回第一个值\n",
    "        如果键不存在，这会 raise KyeError\n",
    "        >>> from StringIO import StringIO\n",
    "        >>> r = Request({'REQUEST_METHOD':'POST', 'wsgi.input':StringIO('a=1&b=M%20M&c=ABC&c=XYZ&e=')})\n",
    "        >>> r['a']\n",
    "        u'1'\n",
    "        >>> r['c']\n",
    "        u'ABC'\n",
    "        >>> r['empty']\n",
    "        Traceback (most recent call last):\n",
    "            ...\n",
    "        KeyError: 'empty'\n",
    "        >>> b = '----WebKitFormBoundaryQQ3J8kPsjFpTmqNz'\n",
    "        >>> pl = ['--%s' % b, 'Content-Disposition: form-data; name=\\\\\"name\\\\\"\\\\n', 'Scofield', '--%s' % b, 'Content-Disposition: form-data; name=\\\\\"name\\\\\"\\\\n', 'Lincoln', '--%s' % b, 'Content-Disposition: form-data; name=\\\\\"file\\\\\"; filename=\\\\\"test.txt\\\\\"', 'Content-Type: text/plain\\\\n', 'just a test', '--%s' % b, 'Content-Disposition: form-data; name=\\\\\"id\\\\\"\\\\n', '4008009001', '--%s--' % b, '']\n",
    "        >>> payload = '\\\\n'.join(pl)\n",
    "        >>> r = Request({'REQUEST_METHOD':'POST', 'CONTENT_LENGTH':str(len(payload)), 'CONTENT_TYPE':'multipart/form-data; boundary=%s' % b, 'wsgi.input':StringIO(payload)})\n",
    "        >>> r.get('name')\n",
    "        u'Scofield'\n",
    "        >>> r.gets('name')\n",
    "        [u'Scofield', u'Lincoln']\n",
    "        >>> f = r.get('file')\n",
    "        >>> f.filename\n",
    "        u'test.txt'\n",
    "        >>> f.file.read()\n",
    "        'just a test'\n",
    "        \"\"\"\n",
    "        r = self._get_raw_input()[key]\n",
    "        if isinstance(r, list):\n",
    "            return r[0]\n",
    "        return r\n",
    "\n",
    "    def get(self, key, default=None):\n",
    "        \"\"\"\n",
    "        实现了字典里面的get功能\n",
    "        和上面的__getitem__一样(request[key]),但如果没有找到key,则返回默认值。\n",
    "        >>> from StringIO import StringIO\n",
    "        >>> r = Request({'REQUEST_METHOD':'POST', 'wsgi.input':StringIO('a=1&b=M%20M&c=ABC&c=XYZ&e=')})\n",
    "        >>> r.get('a')\n",
    "        u'1'\n",
    "        >>> r.get('empty')\n",
    "        >>> r.get('empty', 'DEFAULT')\n",
    "        'DEFAULT'\n",
    "        \"\"\"\n",
    "        r = self._get_raw_input().get(key, default)\n",
    "        if isinstance(r, list):\n",
    "            return r[0]\n",
    "        return r\n",
    "\n",
    "    def gets(self, key):\n",
    "        '''\n",
    "        Get multiple values for specified key.\n",
    "        >>> from StringIO import StringIO\n",
    "        >>> r = Request({'REQUEST_METHOD':'POST', 'wsgi.input':StringIO('a=1&b=M%20M&c=ABC&c=XYZ&e=')})\n",
    "        >>> r.gets('a')\n",
    "        [u'1']\n",
    "        >>> r.gets('c')\n",
    "        [u'ABC', u'XYZ']\n",
    "        >>> r.gets('empty')\n",
    "        Traceback (most recent call last):\n",
    "            ...\n",
    "        KeyError: 'empty'\n",
    "        '''\n",
    "        r = self._get_raw_input()[key]\n",
    "        if isinstance(r, list):\n",
    "            return r[:]\n",
    "        return [r]\n",
    "\n",
    "    def input(self, **kw):\n",
    "        \"\"\"\n",
    "        返回一个由传入的数据和从environ里取出的数据 组成的Dict对象，Dict对象的定义 见db模块\n",
    "        Get input as dict from request, fill dict using provided default value if key not exist.\n",
    "        i = ctx.request.input(role='guest')\n",
    "        i.role ==> 'guest'\n",
    "        >>> from StringIO import StringIO\n",
    "        >>> r = Request({'REQUEST_METHOD':'POST', 'wsgi.input':StringIO('a=1&b=M%20M&c=ABC&c=XYZ&e=')})\n",
    "        >>> i = r.input(x=2008)\n",
    "        >>> i.a\n",
    "        u'1'\n",
    "        >>> i.b\n",
    "        u'M M'\n",
    "        >>> i.c\n",
    "        u'ABC'\n",
    "        >>> i.x\n",
    "        2008\n",
    "        >>> i.get('d', u'100')\n",
    "        u'100'\n",
    "        >>> i.x\n",
    "        2008\n",
    "        \"\"\"\n",
    "        copy = Dict(**kw)\n",
    "        raw = self._get_raw_input()\n",
    "        for k, v in raw.iteritems():\n",
    "            copy[k] = v[0] if isinstance(v, list) else v\n",
    "        return copy\n",
    "\n",
    "    def get_body(self):\n",
    "        \"\"\"\n",
    "        从HTTP POST 请求中取得 body里面的数据，返回为一个str对象\n",
    "        >>> from StringIO import StringIO\n",
    "        >>> r = Request({'REQUEST_METHOD':'POST', 'wsgi.input':StringIO('<xml><raw/>')})\n",
    "        >>> r.get_body()\n",
    "        '<xml><raw/>'\n",
    "        \"\"\"\n",
    "        fp = self._environ['wsgi.input']\n",
    "        return fp.read()\n",
    "\n",
    "    @property\n",
    "    def remote_addr(self):\n",
    "        \"\"\"\n",
    "        Get remote addr. Return '0.0.0.0' if cannot get remote_addr.\n",
    "        >>> r = Request({'REMOTE_ADDR': '192.168.0.100'})\n",
    "        >>> r.remote_addr\n",
    "        '192.168.0.100'\n",
    "        \"\"\"\n",
    "        return self._environ.get('REMOTE_ADDR', '0.0.0.0')\n",
    "\n",
    "    @property\n",
    "    def document_root(self):\n",
    "        \"\"\"\n",
    "        Get raw document_root as str. Return '' if no document_root.\n",
    "        >>> r = Request({'DOCUMENT_ROOT': '/srv/path/to/doc'})\n",
    "        >>> r.document_root\n",
    "        '/srv/path/to/doc'\n",
    "        \"\"\"\n",
    "        return self._environ.get('DOCUMENT_ROOT', '')\n",
    "\n",
    "    @property\n",
    "    def query_string(self):\n",
    "        \"\"\"\n",
    "        Get raw query string as str. Return '' if no query string.\n",
    "        >>> r = Request({'QUERY_STRING': 'a=1&c=2'})\n",
    "        >>> r.query_string\n",
    "        'a=1&c=2'\n",
    "        >>> r = Request({})\n",
    "        >>> r.query_string\n",
    "        ''\n",
    "        \"\"\"\n",
    "        return self._environ.get('QUERY_STRING', '')\n",
    "\n",
    "    @property\n",
    "    def environ(self):\n",
    "        \"\"\"\n",
    "        Get raw environ as dict, both key, value are str.\n",
    "        >>> r = Request({'REQUEST_METHOD': 'GET', 'wsgi.url_scheme':'http'})\n",
    "        >>> r.environ.get('REQUEST_METHOD')\n",
    "        'GET'\n",
    "        >>> r.environ.get('wsgi.url_scheme')\n",
    "        'http'\n",
    "        >>> r.environ.get('SERVER_NAME')\n",
    "        >>> r.environ.get('SERVER_NAME', 'unamed')\n",
    "        'unamed'\n",
    "        \"\"\"\n",
    "        return self._environ\n",
    "\n",
    "    @property\n",
    "    def request_method(self):\n",
    "        \"\"\"\n",
    "        Get request method. The valid returned values are 'GET', 'POST', 'HEAD'.\n",
    "        >>> r = Request({'REQUEST_METHOD': 'GET'})\n",
    "        >>> r.request_method\n",
    "        'GET'\n",
    "        >>> r = Request({'REQUEST_METHOD': 'POST'})\n",
    "        >>> r.request_method\n",
    "        'POST'\n",
    "        \"\"\"\n",
    "        return self._environ['REQUEST_METHOD']\n",
    "\n",
    "    @property\n",
    "    def path_info(self):\n",
    "        \"\"\"\n",
    "        Get request path as str.\n",
    "        >>> r = Request({'PATH_INFO': '/test/a%20b.html'})\n",
    "        >>> r.path_info\n",
    "        '/test/a b.html'\n",
    "        \"\"\"\n",
    "        return urllib.unquote(self._environ.get('PATH_INFO', ''))\n",
    "\n",
    "    @property\n",
    "    def host(self):\n",
    "        \"\"\"\n",
    "        Get request host as str. Default to '' if cannot get host..\n",
    "        >>> r = Request({'HTTP_HOST': 'localhost:8080'})\n",
    "        >>> r.host\n",
    "        'localhost:8080'\n",
    "        \"\"\"\n",
    "        return self._environ.get('HTTP_HOST', '')\n",
    "\n",
    "    def _get_headers(self):\n",
    "        \"\"\"\n",
    "        从environ里 取得HTTP_开通的 header\n",
    "        \"\"\"\n",
    "        if not hasattr(self, '_headers'):\n",
    "            hdrs = {}\n",
    "            for k, v in self._environ.iteritems():\n",
    "                if k.startswith('HTTP_'):\n",
    "                    # convert 'HTTP_ACCEPT_ENCODING' to 'ACCEPT-ENCODING'\n",
    "                    hdrs[k[5:].replace('_', '-').upper()] = v.decode('utf-8')\n",
    "            self._headers = hdrs\n",
    "        return self._headers\n",
    "\n",
    "    @property\n",
    "    def headers(self):\n",
    "        \"\"\"\n",
    "        获取所有的header， setter实现的属性\n",
    "        Get all HTTP headers with key as str and value as unicode. The header names are 'XXX-XXX' uppercase.\n",
    "        >>> r = Request({'HTTP_USER_AGENT': 'Mozilla/5.0', 'HTTP_ACCEPT': 'text/html'})\n",
    "        >>> H = r.headers\n",
    "        >>> H['ACCEPT']\n",
    "        u'text/html'\n",
    "        >>> H['USER-AGENT']\n",
    "        u'Mozilla/5.0'\n",
    "        >>> L = H.items()\n",
    "        >>> L.sort()\n",
    "        >>> L\n",
    "        [('ACCEPT', u'text/html'), ('USER-AGENT', u'Mozilla/5.0')]\n",
    "        \"\"\"\n",
    "        return dict(**self._get_headers())\n",
    "\n",
    "    def header(self, header, default=None):\n",
    "        \"\"\"\n",
    "        获取指定的header的值\n",
    "        Get header from request as unicode, return None if not exist, or default if specified.\n",
    "        The header name is case-insensitive such as 'USER-AGENT' or u'content-Type'.\n",
    "        >>> r = Request({'HTTP_USER_AGENT': 'Mozilla/5.0', 'HTTP_ACCEPT': 'text/html'})\n",
    "        >>> r.header('User-Agent')\n",
    "        u'Mozilla/5.0'\n",
    "        >>> r.header('USER-AGENT')\n",
    "        u'Mozilla/5.0'\n",
    "        >>> r.header('Accept')\n",
    "        u'text/html'\n",
    "        >>> r.header('Test')\n",
    "        >>> r.header('Test', u'DEFAULT')\n",
    "        u'DEFAULT'\n",
    "        \"\"\"\n",
    "        return self._get_headers().get(header.upper(), default)\n",
    "\n",
    "    def _get_cookies(self):\n",
    "        \"\"\"\n",
    "        从environ里取出cookies字符串，并解析成键值对 组成的字典\n",
    "        \"\"\"\n",
    "        if not hasattr(self, '_cookies'):\n",
    "            cookies = {}\n",
    "            cookie_str = self._environ.get('HTTP_COOKIE')\n",
    "            if cookie_str:\n",
    "                for c in cookie_str.split(';'):\n",
    "                    pos = c.find('=')\n",
    "                    if pos > 0:\n",
    "                        cookies[c[:pos].strip()] = utils.unquote(c[pos+1:])\n",
    "            self._cookies = cookies\n",
    "        return self._cookies\n",
    "\n",
    "    @property\n",
    "    def cookies(self):\n",
    "        \"\"\"\n",
    "        setter 以Dict对象返回cookies\n",
    "        Return all cookies as dict. The cookie name is str and values is unicode.\n",
    "        >>> r = Request({'HTTP_COOKIE':'A=123; url=http%3A%2F%2Fwww.example.com%2F'})\n",
    "        >>> r.cookies['A']\n",
    "        u'123'\n",
    "        >>> r.cookies['url']\n",
    "        u'http://www.example.com/'\n",
    "        \"\"\"\n",
    "        return Dict(**self._get_cookies())\n",
    "\n",
    "    def cookie(self, name, default=None):\n",
    "        \"\"\"\n",
    "        获取指定的cookie\n",
    "        Return specified cookie value as unicode. Default to None if cookie not exists.\n",
    "        >>> r = Request({'HTTP_COOKIE':'A=123; url=http%3A%2F%2Fwww.example.com%2F'})\n",
    "        >>> r.cookie('A')\n",
    "        u'123'\n",
    "        >>> r.cookie('url')\n",
    "        u'http://www.example.com/'\n",
    "        >>> r.cookie('test')\n",
    "        >>> r.cookie('test', u'DEFAULT')\n",
    "        u'DEFAULT'\n",
    "        \"\"\"\n",
    "        return self._get_cookies().get(name, default)\n",
    "\n",
    "\n",
    "class Response(object):\n",
    "\n",
    "    def __init__(self):\n",
    "        self._status = '200 OK'\n",
    "        self._headers = {'CONTENT-TYPE': 'text/html; charset=utf-8'}\n",
    "\n",
    "    def unset_header(self, name):\n",
    "        \"\"\"\n",
    "        删除指定的header\n",
    "        >>> r = Response()\n",
    "        >>> r.header('content-type')\n",
    "        'text/html; charset=utf-8'\n",
    "        >>> r.unset_header('CONTENT-type')\n",
    "        >>> r.header('content-type')\n",
    "        \"\"\"\n",
    "        key = name.upper()\n",
    "        if key not in _RESPONSE_HEADER_DICT:\n",
    "            key = name\n",
    "        if key in self._headers:\n",
    "            del self._headers[key]\n",
    "\n",
    "    def set_header(self, name, value):\n",
    "        \"\"\"\n",
    "        给指定的header 赋值\n",
    "        >>> r = Response()\n",
    "        >>> r.header('content-type')\n",
    "        'text/html; charset=utf-8'\n",
    "        >>> r.set_header('CONTENT-type', 'image/png')\n",
    "        >>> r.header('content-TYPE')\n",
    "        'image/png'\n",
    "        \"\"\"\n",
    "        key = name.upper()\n",
    "        if key not in _RESPONSE_HEADER_DICT:\n",
    "            key = name\n",
    "        self._headers[key] = utils.to_str(value)\n",
    "\n",
    "    def header(self, name):\n",
    "        \"\"\"\n",
    "        获取Response Header 里单个 Header的值， 非大小写敏感\n",
    "        >>> r = Response()\n",
    "        >>> r.header('content-type')\n",
    "        'text/html; charset=utf-8'\n",
    "        >>> r.header('CONTENT-type')\n",
    "        'text/html; charset=utf-8'\n",
    "        >>> r.header('X-Powered-By')\n",
    "        \"\"\"\n",
    "        key = name.upper()\n",
    "        if key not in _RESPONSE_HEADER_DICT:\n",
    "            key = name\n",
    "        return self._headers.get(key)\n",
    "\n",
    "    @property\n",
    "    def headers(self):\n",
    "        \"\"\"\n",
    "        setter 构造的属性，以[(key1, value1), (key2, value2)...] 形式存储 所有header的值，\n",
    "        包括cookies的值\n",
    "        >>> r = Response()\n",
    "        >>> r.headers\n",
    "        [('Content-Type', 'text/html; charset=utf-8'), ('X-Powered-By', 'transwarp/1.0')]\n",
    "        >>> r.set_cookie('s1', 'ok', 3600)\n",
    "        >>> r.headers\n",
    "        [('Content-Type', 'text/html; charset=utf-8'), ('Set-Cookie', 's1=ok; Max-Age=3600; Path=/; HttpOnly'), ('X-Powered-By', 'transwarp/1.0')]\n",
    "        \"\"\"\n",
    "        L = [(_RESPONSE_HEADER_DICT.get(k, k), v) for k, v in self._headers.iteritems()]\n",
    "        if hasattr(self, '_cookies'):\n",
    "            for v in self._cookies.itervalues():\n",
    "                L.append(('Set-Cookie', v))\n",
    "        L.append(_HEADER_X_POWERED_BY)\n",
    "        return L\n",
    "\n",
    "    @property\n",
    "    def content_type(self):\n",
    "        \"\"\"\n",
    "        setter 方法实现的属性，用户保存header： Content-Type的值\n",
    "        >>> r = Response()\n",
    "        >>> r.content_type\n",
    "        'text/html; charset=utf-8'\n",
    "        >>> r.content_type = 'application/json'\n",
    "        >>> r.content_type\n",
    "        'application/json'\n",
    "        \"\"\"\n",
    "        return self.header('CONTENT-TYPE')\n",
    "\n",
    "    @content_type.setter\n",
    "    def content_type(self, value):\n",
    "        \"\"\"\n",
    "        让content_type 属性可写， 及设置Content-Type Header\n",
    "        \"\"\"\n",
    "        if value:\n",
    "            self.set_header('CONTENT-TYPE', value)\n",
    "        else:\n",
    "            self.unset_header('CONTENT-TYPE')\n",
    "\n",
    "    @property\n",
    "    def content_length(self):\n",
    "        \"\"\"\n",
    "        获取Content-Length Header 的值\n",
    "        >>> r = Response()\n",
    "        >>> r.content_length\n",
    "        >>> r.content_length = 100\n",
    "        >>> r.content_length\n",
    "        '100'\n",
    "        \"\"\"\n",
    "        return self.header('CONTENT-LENGTH')\n",
    "\n",
    "    @content_length.setter\n",
    "    def content_length(self, value):\n",
    "        \"\"\"\n",
    "        设置Content-Length Header 的值\n",
    "        >>> r = Response()\n",
    "        >>> r.content_length = '1024'\n",
    "        >>> r.content_length\n",
    "        '1024'\n",
    "        >>> r.content_length = 1024 * 8\n",
    "        >>> r.content_length\n",
    "        '8192'\n",
    "        \"\"\"\n",
    "        self.set_header('CONTENT-LENGTH', str(value))\n",
    "\n",
    "    def delete_cookie(self, name):\n",
    "        \"\"\"\n",
    "        Delete a cookie immediately.\n",
    "        Args:\n",
    "          name: the cookie name.\n",
    "        \"\"\"\n",
    "        self.set_cookie(name, '__deleted__', expires=0)\n",
    "\n",
    "    def set_cookie(self, name, value, max_age=None, expires=None, path='/', domain=None, secure=False, http_only=True):\n",
    "        \"\"\"\n",
    "        Set a cookie.\n",
    "        Args:\n",
    "          name: the cookie name.\n",
    "          value: the cookie value.\n",
    "          max_age: optional, seconds of cookie's max age.\n",
    "          expires: optional, unix timestamp, datetime or date object that indicate an absolute time of the\n",
    "                   expiration time of cookie. Note that if expires specified, the max_age will be ignored.\n",
    "          path: the cookie path, default to '/'.\n",
    "          domain: the cookie domain, default to None.\n",
    "          secure: if the cookie secure, default to False.\n",
    "          http_only: if the cookie is for http only, default to True for better safty\n",
    "                     (client-side script cannot access cookies with HttpOnly flag).\n",
    "        >>> r = Response()\n",
    "        >>> r.set_cookie('company', 'Abc, Inc.', max_age=3600)\n",
    "        >>> r._cookies\n",
    "        {'company': 'company=Abc%2C%20Inc.; Max-Age=3600; Path=/; HttpOnly'}\n",
    "        >>> r.set_cookie('company', r'Example=\"Limited\"', expires=1342274794.123, path='/sub/')\n",
    "        >>> r._cookies\n",
    "        {'company': 'company=Example%3D%22Limited%22; Expires=Sat, 14-Jul-2012 14:06:34 GMT; Path=/sub/; HttpOnly'}\n",
    "        >>> dt = datetime.datetime(2012, 7, 14, 22, 6, 34, tzinfo=UTC('+8:00'))\n",
    "        >>> r.set_cookie('company', 'Expires', expires=dt)\n",
    "        >>> r._cookies\n",
    "        {'company': 'company=Expires; Expires=Sat, 14-Jul-2012 14:06:34 GMT; Path=/; HttpOnly'}\n",
    "        \"\"\"\n",
    "        if not hasattr(self, '_cookies'):\n",
    "            self._cookies = {}\n",
    "        L = ['%s=%s' % (utils.quote(name), utils.quote(value))]\n",
    "        if expires is not None:\n",
    "            if isinstance(expires, (float, int, long)):\n",
    "                L.append('Expires=%s' % datetime.datetime.fromtimestamp(expires, UTC_0).strftime('%a, %d-%b-%Y %H:%M:%S GMT'))\n",
    "            if isinstance(expires, (datetime.date, datetime.datetime)):\n",
    "                L.append('Expires=%s' % expires.astimezone(UTC_0).strftime('%a, %d-%b-%Y %H:%M:%S GMT'))\n",
    "        elif isinstance(max_age, (int, long)):\n",
    "            L.append('Max-Age=%d' % max_age)\n",
    "        L.append('Path=%s' % path)\n",
    "        if domain:\n",
    "            L.append('Domain=%s' % domain)\n",
    "        if secure:\n",
    "            L.append('Secure')\n",
    "        if http_only:\n",
    "            L.append('HttpOnly')\n",
    "        self._cookies[name] = '; '.join(L)\n",
    "\n",
    "    def unset_cookie(self, name):\n",
    "        \"\"\"\n",
    "        Unset a cookie.\n",
    "        >>> r = Response()\n",
    "        >>> r.set_cookie('company', 'Abc, Inc.', max_age=3600)\n",
    "        >>> r._cookies\n",
    "        {'company': 'company=Abc%2C%20Inc.; Max-Age=3600; Path=/; HttpOnly'}\n",
    "        >>> r.unset_cookie('company')\n",
    "        >>> r._cookies\n",
    "        {}\n",
    "        \"\"\"\n",
    "        if hasattr(self, '_cookies'):\n",
    "            if name in self._cookies:\n",
    "                del self._cookies[name]\n",
    "\n",
    "    @property\n",
    "    def status_code(self):\n",
    "        \"\"\"\n",
    "        Get response status code as int.\n",
    "        >>> r = Response()\n",
    "        >>> r.status_code\n",
    "        200\n",
    "        >>> r.status = 404\n",
    "        >>> r.status_code\n",
    "        404\n",
    "        >>> r.status = '500 Internal Error'\n",
    "        >>> r.status_code\n",
    "        500\n",
    "        \"\"\"\n",
    "        return int(self._status[:3])\n",
    "\n",
    "    @property\n",
    "    def status(self):\n",
    "        \"\"\"\n",
    "        Get response status. Default to '200 OK'.\n",
    "        >>> r = Response()\n",
    "        >>> r.status\n",
    "        '200 OK'\n",
    "        >>> r.status = 404\n",
    "        >>> r.status\n",
    "        '404 Not Found'\n",
    "        >>> r.status = '500 Oh My God'\n",
    "        >>> r.status\n",
    "        '500 Oh My God'\n",
    "        \"\"\"\n",
    "        return self._status\n",
    "\n",
    "    @status.setter\n",
    "    def status(self, value):\n",
    "        \"\"\"\n",
    "        Set response status as int or str.\n",
    "        >>> r = Response()\n",
    "        >>> r.status = 404\n",
    "        >>> r.status\n",
    "        '404 Not Found'\n",
    "        >>> r.status = '500 ERR'\n",
    "        >>> r.status\n",
    "        '500 ERR'\n",
    "        >>> r.status = u'403 Denied'\n",
    "        >>> r.status\n",
    "        '403 Denied'\n",
    "        >>> r.status = 99\n",
    "        Traceback (most recent call last):\n",
    "          ...\n",
    "        ValueError: Bad response code: 99\n",
    "        >>> r.status = 'ok'\n",
    "        Traceback (most recent call last):\n",
    "          ...\n",
    "        ValueError: Bad response code: ok\n",
    "        >>> r.status = [1, 2, 3]\n",
    "        Traceback (most recent call last):\n",
    "          ...\n",
    "        TypeError: Bad type of response code.\n",
    "        \"\"\"\n",
    "        if isinstance(value, (int, long)):\n",
    "            if 100 <= value <= 999:\n",
    "                st = _RESPONSE_STATUSES.get(value, '')\n",
    "                if st:\n",
    "                    self._status = '%d %s' % (value, st)\n",
    "                else:\n",
    "                    self._status = str(value)\n",
    "            else:\n",
    "                raise ValueError('Bad response code: %d' % value)\n",
    "        elif isinstance(value, basestring):\n",
    "            if isinstance(value, unicode):\n",
    "                value = value.encode('utf-8')\n",
    "            if _RE_RESPONSE_STATUS.match(value):\n",
    "                self._status = value\n",
    "            else:\n",
    "                raise ValueError('Bad response code: %s' % value)\n",
    "        else:\n",
    "            raise TypeError('Bad type of response code.')\n",
    "\n",
    "\n",
    "#################################################################\n",
    "# 实现URL路由功能\n",
    "# 将URL 映射到 函数上\n",
    "#################################################################\n",
    "# 用于捕获变量的re\n",
    "_re_route = re.compile(r'(:[a-zA-Z_]\\w*)')\n",
    "\n",
    "\n",
    "# 方法的装饰器，用于捕获url\n",
    "def get(path):\n",
    "    \"\"\"\n",
    "    A @get decorator.\n",
    "    @get('/:id')\n",
    "    def index(id):\n",
    "        pass\n",
    "    >>> @get('/test/:id')\n",
    "    ... def test():\n",
    "    ...     return 'ok'\n",
    "    ...\n",
    "    >>> test.__web_route__\n",
    "    '/test/:id'\n",
    "    >>> test.__web_method__\n",
    "    'GET'\n",
    "    >>> test()\n",
    "    'ok'\n",
    "    \"\"\"\n",
    "    def _decorator(func):\n",
    "        func.__web_route__ = path\n",
    "        func.__web_method__ = 'GET'\n",
    "        return func\n",
    "    return _decorator\n",
    "\n",
    "\n",
    "def post(path):\n",
    "    \"\"\"\n",
    "    A @post decorator.\n",
    "    >>> @post('/post/:id')\n",
    "    ... def testpost():\n",
    "    ...     return '200'\n",
    "    ...\n",
    "    >>> testpost.__web_route__\n",
    "    '/post/:id'\n",
    "    >>> testpost.__web_method__\n",
    "    'POST'\n",
    "    >>> testpost()\n",
    "    '200'\n",
    "    \"\"\"\n",
    "    def _decorator(func):\n",
    "        func.__web_route__ = path\n",
    "        func.__web_method__ = 'POST'\n",
    "        return func\n",
    "    return _decorator\n",
    "\n",
    "\n",
    "def _build_regex(path):\n",
    "    r\"\"\"\n",
    "    用于将路径转换成正则表达式，并捕获其中的参数\n",
    "    >>> _build_regex('/path/to/:file')\n",
    "    '^\\\\/path\\\\/to\\\\/(?P<file>[^\\\\/]+)$'\n",
    "    >>> _build_regex('/:user/:comments/list')\n",
    "    '^\\\\/(?P<user>[^\\\\/]+)\\\\/(?P<comments>[^\\\\/]+)\\\\/list$'\n",
    "    >>> _build_regex(':id-:pid/:w')\n",
    "    '^(?P<id>[^\\\\/]+)\\\\-(?P<pid>[^\\\\/]+)\\\\/(?P<w>[^\\\\/]+)$'\n",
    "    \"\"\"\n",
    "    re_list = ['^']\n",
    "    var_list = []\n",
    "    is_var = False\n",
    "    for v in _re_route.split(path):\n",
    "        if is_var:\n",
    "            var_name = v[1:]\n",
    "            var_list.append(var_name)\n",
    "            re_list.append(r'(?P<%s>[^\\/]+)' % var_name)\n",
    "        else:\n",
    "            s = ''\n",
    "            for ch in v:\n",
    "                if '0' <= ch <= '9':\n",
    "                    s += ch\n",
    "                elif 'A' <= ch <= 'Z':\n",
    "                    s += ch\n",
    "                elif 'a' <= ch <= 'z':\n",
    "                    s += ch\n",
    "                else:\n",
    "                    s = s + '\\\\' + ch\n",
    "            re_list.append(s)\n",
    "        is_var = not is_var\n",
    "    re_list.append('$')\n",
    "    return ''.join(re_list)\n",
    "\n",
    "\n",
    "def _static_file_generator(fpath, block_size=8192):\n",
    "    \"\"\"\n",
    "    读取静态文件的一个生成器\n",
    "    \"\"\"\n",
    "    with open(fpath, 'rb') as f:\n",
    "        block = f.read(block_size)\n",
    "        while block:\n",
    "            yield block\n",
    "            block = f.read(block_size)\n",
    "\n",
    "\n",
    "class Route(object):\n",
    "    \"\"\"\n",
    "    动态路由对象，处理 装饰器捕获的url 和 函数\n",
    "    比如：\n",
    "            @get('/:id')\n",
    "                def index(id):\n",
    "                pass\n",
    "    在构造器中 path、method、is_static、route 和url相关\n",
    "    而 func 则指的装饰器里的func，比如上面的index函数\n",
    "    \"\"\"\n",
    "\n",
    "    def __init__(self, func):\n",
    "        \"\"\"\n",
    "        path： 通过method的装饰器捕获的path\n",
    "        method： 通过method装饰器捕获的method\n",
    "        is_static： 路径是否含变量，含变量为True\n",
    "        route：动态url（含变量）则捕获其变量的 re\n",
    "        func： 方法装饰器里定义的函数\n",
    "        \"\"\"\n",
    "        self.path = func.__web_route__\n",
    "        self.method = func.__web_method__\n",
    "        self.is_static = _re_route.search(self.path) is None\n",
    "        if not self.is_static:\n",
    "            self.route = re.compile(_build_regex(self.path))\n",
    "        self.func = func\n",
    "\n",
    "    def match(self, url):\n",
    "        \"\"\"\n",
    "        传入url，返回捕获的变量\n",
    "        \"\"\"\n",
    "        m = self.route.match(url)\n",
    "        if m:\n",
    "            return m.groups()\n",
    "        return None\n",
    "\n",
    "    def __call__(self, *args):\n",
    "        \"\"\"\n",
    "        实例对象直接调用时，执行传入的函数对象\n",
    "        \"\"\"\n",
    "        return self.func(*args)\n",
    "\n",
    "    def __str__(self):\n",
    "        if self.is_static:\n",
    "            return 'Route(static,%s,path=%s)' % (self.method, self.path)\n",
    "        return 'Route(dynamic,%s,path=%s)' % (self.method, self.path)\n",
    "\n",
    "    __repr__ = __str__\n",
    "\n",
    "\n",
    "class StaticFileRoute(object):\n",
    "    \"\"\"\n",
    "    静态文件路由对象，和Route相对应\n",
    "    \"\"\"\n",
    "    def __init__(self):\n",
    "        self.method = 'GET'\n",
    "        self.is_static = False\n",
    "        self.route = re.compile('^/static/(.+)$')\n",
    "\n",
    "    def match(self, url):\n",
    "        if url.startswith('/static/'):\n",
    "            return (url[1:], )\n",
    "        return None\n",
    "\n",
    "    def __call__(self, *args):\n",
    "        fpath = os.path.join(ctx.application.document_root, args[0])\n",
    "        if not os.path.isfile(fpath):\n",
    "            raise HttpError.notfound()\n",
    "        fext = os.path.splitext(fpath)[1]\n",
    "        ctx.response.content_type = mimetypes.types_map.get(fext.lower(), 'application/octet-stream')\n",
    "        return _static_file_generator(fpath)\n",
    "\n",
    "\n",
    "class MultipartFile(object):\n",
    "    \"\"\"\n",
    "    Multipart file storage get from request input.\n",
    "    f = ctx.request['file']\n",
    "    f.filename # 'test.png'\n",
    "    f.file # file-like object\n",
    "    \"\"\"\n",
    "    def __init__(self, storage):\n",
    "        self.filename = utils.to_unicode(storage.filename)\n",
    "        self.file = storage.file\n",
    "\n",
    "\n",
    "#################################################################\n",
    "# 实现视图功能\n",
    "# 主要涉及到模板引擎和View装饰器的实现\n",
    "#################################################################\n",
    "class Template(object):\n",
    "\n",
    "    def __init__(self, template_name, **kw):\n",
    "        \"\"\"\n",
    "        Init a template object with template name, model as dict, and additional kw that will append to model.\n",
    "        >>> t = Template('hello.html', title='Hello', copyright='@2012')\n",
    "        >>> t.model['title']\n",
    "        'Hello'\n",
    "        >>> t.model['copyright']\n",
    "        '@2012'\n",
    "        >>> t = Template('test.html', abc=u'ABC', xyz=u'XYZ')\n",
    "        >>> t.model['abc']\n",
    "        u'ABC'\n",
    "        \"\"\"\n",
    "        self.template_name = template_name\n",
    "        self.model = dict(**kw)\n",
    "\n",
    "\n",
    "class TemplateEngine(object):\n",
    "    \"\"\"\n",
    "    Base template engine.\n",
    "    \"\"\"\"\"\n",
    "    def __call__(self, path, model):\n",
    "        return '<!-- override this method to render template -->'\n",
    "\n",
    "\n",
    "class Jinja2TemplateEngine(TemplateEngine):\n",
    "    \"\"\"\n",
    "    Render using jinja2 template engine.\n",
    "    >>> templ_path = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), 'test')\n",
    "    >>> engine = Jinja2TemplateEngine(templ_path)\n",
    "    >>> engine.add_filter('datetime', lambda dt: dt.strftime('%Y-%m-%d %H:%M:%S'))\n",
    "    >>> engine('jinja2-test.html', dict(name='Michael', posted_at=datetime.datetime(2014, 6, 1, 10, 11, 12)))\n",
    "    '<p>Hello, Michael.</p><span>2014-06-01 10:11:12</span>'\n",
    "    \"\"\"\n",
    "\n",
    "    def __init__(self, templ_dir, **kw):\n",
    "        from jinja2 import Environment, FileSystemLoader\n",
    "        if 'autoescape' not in kw:\n",
    "            kw['autoescape'] = True\n",
    "        self._env = Environment(loader=FileSystemLoader(templ_dir), **kw)\n",
    "\n",
    "    def add_filter(self, name, fn_filter):\n",
    "        self._env.filters[name] = fn_filter\n",
    "\n",
    "    def __call__(self, path, model):\n",
    "        return self._env.get_template(path).render(**model).encode('utf-8')\n",
    "\n",
    "\n",
    "def _debug():\n",
    "    \"\"\"\n",
    "    :return:\n",
    "    \"\"\"\n",
    "    pass\n",
    "\n",
    "\n",
    "def _default_error_handler(e, start_response, is_debug):\n",
    "    \"\"\"\n",
    "    用于处理异常，主要是响应一个异常页面\n",
    "    :param e:\n",
    "    :param start_response: wsgi里面的 start_response 函数\n",
    "    :param is_debug:\n",
    "    :return:\n",
    "    \"\"\"\n",
    "    if isinstance(e, HttpError):\n",
    "        logging.info('HttpError: %s' % e.status)\n",
    "        headers = e.headers[:]\n",
    "        headers.append(('Content-Type', 'text/html'))\n",
    "        start_response(e.status, headers)\n",
    "        return ('<html><body><h1>%s</h1></body></html>' % e.status)\n",
    "    logging.exception('Exception:')\n",
    "    start_response('500 Internal Server Error', [('Content-Type', 'text/html'), _HEADER_X_POWERED_BY])\n",
    "    if is_debug:\n",
    "        return _debug()\n",
    "    return ('<html><body><h1>500 Internal Server Error</h1><h3>%s</h3></body></html>' % str(e))\n",
    "\n",
    "\n",
    "def view(path):\n",
    "    \"\"\"\n",
    "    被装饰的函数 需要返回一个字典对象，用于渲染\n",
    "    装饰器通过Template类将 path 和 dict 关联在一个 Template对象上\n",
    "    A view decorator that render a view by dict.\n",
    "    >>> @view('test/view.html')\n",
    "    ... def hello():\n",
    "    ...     return dict(name='Bob')\n",
    "    >>> t = hello()\n",
    "    >>> isinstance(t, Template)\n",
    "    True\n",
    "    >>> t.template_name\n",
    "    'test/view.html'\n",
    "    >>> @view('test/view.html')\n",
    "    ... def hello2():\n",
    "    ...     return ['a list']\n",
    "    >>> t = hello2()\n",
    "    Traceback (most recent call last):\n",
    "      ...\n",
    "    ValueError: Expect return a dict when using @view() decorator.\n",
    "    \"\"\"\n",
    "    def _decorator(func):\n",
    "        @functools.wraps(func)\n",
    "        def _wrapper(*args, **kw):\n",
    "            r = func(*args, **kw)\n",
    "            if isinstance(r, dict):\n",
    "                logging.info('return Template')\n",
    "                return Template(path, **r)\n",
    "            raise ValueError('Expect return a dict when using @view() decorator.')\n",
    "        return _wrapper\n",
    "    return _decorator\n",
    "\n",
    "\n",
    "#################################################################\n",
    "# 实现URL拦截器\n",
    "# 主要interceptor的实现\n",
    "#################################################################\n",
    "_RE_INTERCEPTOR_STARTS_WITH = re.compile(r'^([^\\*\\?]+)\\*?$')\n",
    "_RE_INTERCEPTOR_ENDS_WITH = re.compile(r'^\\*([^\\*\\?]+)$')\n",
    "\n",
    "\n",
    "def _build_pattern_fn(pattern):\n",
    "    \"\"\"\n",
    "    传入需要匹配的字符串： URL\n",
    "    返回一个函数，该函数接收一个字符串参数，检测该字符串是否\n",
    "    符合pattern\n",
    "    \"\"\"\n",
    "    m = _RE_INTERCEPTOR_STARTS_WITH.match(pattern)\n",
    "    if m:\n",
    "        return lambda p: p.startswith(m.group(1))\n",
    "    m = _RE_INTERCEPTOR_ENDS_WITH.match(pattern)\n",
    "    if m:\n",
    "        return lambda p: p.endswith(m.group(1))\n",
    "    raise ValueError('Invalid pattern definition in interceptor.')\n",
    "\n",
    "\n",
    "def interceptor(pattern='/'):\n",
    "    \"\"\"\n",
    "    An @interceptor decorator.\n",
    "    @interceptor('/admin/')\n",
    "    def check_admin(req, resp):\n",
    "        pass\n",
    "    \"\"\"\n",
    "    def _decorator(func):\n",
    "        func.__interceptor__ = _build_pattern_fn(pattern)\n",
    "        return func\n",
    "    return _decorator\n",
    "\n",
    "\n",
    "def _build_interceptor_fn(func, next):\n",
    "    \"\"\"\n",
    "    拦截器接受一个next函数，这样，一个拦截器可以决定调用next()继续处理请求还是直接返回\n",
    "    \"\"\"\n",
    "\n",
    "    def _wrapper():\n",
    "        if func.__interceptor__(ctx.request.path_info):\n",
    "            return func(next)\n",
    "        else:\n",
    "            return next()\n",
    "    return _wrapper\n",
    "\n",
    "\n",
    "def _build_interceptor_chain(last_fn, *interceptors):\n",
    "    \"\"\"\n",
    "    Build interceptor chain.\n",
    "    >>> def target():\n",
    "    ...     print 'target'\n",
    "    ...     return 123\n",
    "    >>> @interceptor('/')\n",
    "    ... def f1(next):\n",
    "    ...     print 'before f1()'\n",
    "    ...     return next()\n",
    "    >>> @interceptor('/test/')\n",
    "    ... def f2(next):\n",
    "    ...     print 'before f2()'\n",
    "    ...     try:\n",
    "    ...         return next()\n",
    "    ...     finally:\n",
    "    ...         print 'after f2()'\n",
    "    >>> @interceptor('/')\n",
    "    ... def f3(next):\n",
    "    ...     print 'before f3()'\n",
    "    ...     try:\n",
    "    ...         return next()\n",
    "    ...     finally:\n",
    "    ...         print 'after f3()'\n",
    "    >>> chain = _build_interceptor_chain(target, f1, f2, f3)\n",
    "    >>> ctx.request = Dict(path_info='/test/abc')\n",
    "    >>> chain()\n",
    "    before f1()\n",
    "    before f2()\n",
    "    before f3()\n",
    "    target\n",
    "    after f3()\n",
    "    after f2()\n",
    "    123\n",
    "    >>> ctx.request = Dict(path_info='/api/')\n",
    "    >>> chain()\n",
    "    before f1()\n",
    "    before f3()\n",
    "    target\n",
    "    after f3()\n",
    "    123\n",
    "    \"\"\"\n",
    "    L = list(interceptors)\n",
    "    L.reverse()\n",
    "    fn = last_fn\n",
    "    for f in L:\n",
    "        fn = _build_interceptor_fn(f, fn)\n",
    "    return fn\n",
    "\n",
    "\n",
    "def _load_module(module_name):\n",
    "    \"\"\"\n",
    "    Load module from name as str.\n",
    "    >>> m = _load_module('xml')\n",
    "    >>> m.__name__\n",
    "    'xml'\n",
    "    >>> m = _load_module('xml.sax')\n",
    "    >>> m.__name__\n",
    "    'xml.sax'\n",
    "    >>> m = _load_module('xml.sax.handler')\n",
    "    >>> m.__name__\n",
    "    'xml.sax.handler'\n",
    "    \"\"\"\n",
    "    last_dot = module_name.rfind('.')\n",
    "    if last_dot == (-1):\n",
    "        return __import__(module_name, globals(), locals())\n",
    "    from_module = module_name[:last_dot]\n",
    "    import_module = module_name[last_dot+1:]\n",
    "    m = __import__(from_module, globals(), locals(), [import_module])\n",
    "    return getattr(m, import_module)\n",
    "\n",
    "\n",
    "#################################################################\n",
    "# 全局WSGIApplication的类，实现WSGI接口\n",
    "# WSGIApplication 封装了 wsgi Server（run方法） 和 wsgi 处理函数（wsgi静态方法）\n",
    "# 上面的所有的功能都是对 wsgi 处理函数的装饰\n",
    "#################################################################\n",
    "class WSGIApplication(object):\n",
    "\n",
    "    def __init__(self, document_root=None, **kw):\n",
    "        \"\"\"\n",
    "        Init a WSGIApplication.\n",
    "        Args:\n",
    "          document_root: document root path.\n",
    "        \"\"\"\n",
    "        self._running = False\n",
    "        self._document_root = document_root\n",
    "\n",
    "        self._interceptors = []\n",
    "        self._template_engine = None\n",
    "\n",
    "        self._get_static = {}\n",
    "        self._post_static = {}\n",
    "\n",
    "        self._get_dynamic = []\n",
    "        self._post_dynamic = []\n",
    "\n",
    "    def _check_not_running(self):\n",
    "        \"\"\"\n",
    "        检测app对象 是否运行\n",
    "        \"\"\"\n",
    "        if self._running:\n",
    "            raise RuntimeError('Cannot modify WSGIApplication when running.')\n",
    "\n",
    "    @property\n",
    "    def template_engine(self):\n",
    "        return self._template_engine\n",
    "\n",
    "    @template_engine.setter\n",
    "    def template_engine(self, engine):\n",
    "        \"\"\"\n",
    "        设置app 使用的模板引擎\n",
    "        \"\"\"\n",
    "        self._check_not_running()\n",
    "        self._template_engine = engine\n",
    "\n",
    "    def add_module(self, mod):\n",
    "        self._check_not_running()\n",
    "        m = mod if type(mod) == types.ModuleType else _load_module(mod)\n",
    "        logging.info('Add module: %s' % m.__name__)\n",
    "        for name in dir(m):\n",
    "            fn = getattr(m, name)\n",
    "            if callable(fn) and hasattr(fn, '__web_route__') and hasattr(fn, '__web_method__'):\n",
    "                self.add_url(fn)\n",
    "\n",
    "    def add_url(self, func):\n",
    "        \"\"\"\n",
    "        添加URL，主要是添加路由\n",
    "        \"\"\"\n",
    "        self._check_not_running()\n",
    "        route = Route(func)\n",
    "        if route.is_static:\n",
    "            if route.method == 'GET':\n",
    "                self._get_static[route.path] = route\n",
    "            if route.method == 'POST':\n",
    "                self._post_static[route.path] = route\n",
    "        else:\n",
    "            if route.method == 'GET':\n",
    "                self._get_dynamic.append(route)\n",
    "            if route.method == 'POST':\n",
    "                self._post_dynamic.append(route)\n",
    "        logging.info('Add route: %s' % str(route))\n",
    "\n",
    "    def add_interceptor(self, func):\n",
    "        \"\"\"\n",
    "        添加拦截器\n",
    "        \"\"\"\n",
    "        self._check_not_running()\n",
    "        self._interceptors.append(func)\n",
    "        logging.info('Add interceptor: %s' % str(func))\n",
    "\n",
    "    def run(self, port=9000, host='127.0.0.1'):\n",
    "        \"\"\"\n",
    "        启动python自带的WSGI Server\n",
    "        \"\"\"\n",
    "        from wsgiref.simple_server import make_server\n",
    "        logging.info('application (%s) will start at %s:%s...' % (self._document_root, host, port))\n",
    "        server = make_server(host, port, self.get_wsgi_application(debug=True))\n",
    "        server.serve_forever()\n",
    "\n",
    "    def get_wsgi_application(self, debug=False):\n",
    "        self._check_not_running()\n",
    "        if debug:\n",
    "            self._get_dynamic.append(StaticFileRoute())\n",
    "        self._running = True\n",
    "\n",
    "        _application = Dict(document_root=self._document_root)\n",
    "\n",
    "        def fn_route():\n",
    "            request_method = ctx.request.request_method\n",
    "            path_info = ctx.request.path_info\n",
    "            if request_method=='GET':\n",
    "                fn = self._get_static.get(path_info, None)\n",
    "                if fn:\n",
    "                    return fn()\n",
    "                for fn in self._get_dynamic:\n",
    "                    args = fn.match(path_info)\n",
    "                    if args:\n",
    "                        return fn(*args)\n",
    "                raise HttpError.notfound()\n",
    "            if request_method=='POST':\n",
    "                fn = self._post_static.get(path_info, None)\n",
    "                if fn:\n",
    "                    return fn()\n",
    "                for fn in self._post_dynamic:\n",
    "                    args = fn.match(path_info)\n",
    "                    if args:\n",
    "                        return fn(*args)\n",
    "                raise HttpError.notfound()\n",
    "            raise badrequest()\n",
    "\n",
    "        fn_exec = _build_interceptor_chain(fn_route, *self._interceptors)\n",
    "        fn_exec = _build_interceptor_chain(fn_route, *self._interceptors)\n",
    "\n",
    "        def wsgi(env, start_response):\n",
    "            \"\"\"\n",
    "            WSGI 处理函数\n",
    "            \"\"\"\n",
    "            ctx.application = _application\n",
    "            ctx.request = Request(env)\n",
    "            response = ctx.response = Response()\n",
    "            try:\n",
    "                r = fn_exec()\n",
    "                if isinstance(r, Template):\n",
    "                    r = self._template_engine(r.template_name, r.model)\n",
    "                if isinstance(r, unicode):\n",
    "                    r = r.encode('utf-8')\n",
    "                if r is None:\n",
    "                    r = []\n",
    "                start_response(response.status, response.headers)\n",
    "                return r\n",
    "            except _RedirectError, e:\n",
    "                response.set_header('Location', e.location)\n",
    "                start_response(e.status, response.headers)\n",
    "                return []\n",
    "            except _HttpError, e:\n",
    "                start_response(e.status, response.headers)\n",
    "                return ['<html><body><h1>', e.status, '</h1></body></html>']\n",
    "            except Exception, e:\n",
    "                logging.exception(e)\n",
    "                if not debug:\n",
    "                    start_response('500 Internal Server Error', [])\n",
    "                    return ['<html><body><h1>500 Internal Server Error</h1></body></html>']\n",
    "                exc_type, exc_value, exc_traceback = sys.exc_info()\n",
    "                fp = StringIO()\n",
    "                traceback.print_exception(exc_type, exc_value, exc_traceback, file=fp)\n",
    "                stacks = fp.getvalue()\n",
    "                fp.close()\n",
    "                start_response('500 Internal Server Error', [])\n",
    "                return [\n",
    "                    r'''<html><body><h1>500 Internal Server Error</h1><div style=\"font-family:Monaco, Menlo, Consolas, 'Courier New', monospace;\"><pre>''',\n",
    "                    stacks.replace('<', '&lt;').replace('>', '&gt;'),\n",
    "                    '</pre></div></body></html>']\n",
    "            finally:\n",
    "                del ctx.application\n",
    "                del ctx.request\n",
    "                del ctx.response\n",
    "\n",
    "        return wsgi\n",
    "\n",
    "if __name__ == '__main__':\n",
    "    sys.path.append('.')\n",
    "    import doctest\n",
    "    doctest.testmod()"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 2",
   "language": "python",
   "name": "python2"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 2
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython2",
   "version": "2.7.13"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
